Nevron Open Vision Documentation
Framework / Compression / Zip Compression
In This Topic
    Zip Compression
    In This Topic

    The zip file format is widely used because of its speed and good compression ratio. That is why in many scenarios applications need the ability to compress and decompress zip archives. Nevron Open Vision provides full support for compressing and decompressing zip archives, including decompressing only individual files from an archive which leads to better performance when only specific files need to be extracted from the archive.

     Zip Items

    Both ZIP compression and decompression of NOV work with ZIP items. A ZIP item is basically a named stream, which the NOV ZIP engine can compress/decompress. A ZIP item exposes the following properties:

    • Name - the name of the item. This is also the name of the ZIP entry in the ZIP archive.
    • Stream - the data of the item. This is the data the ZIP compressor should compress and the decompressed by the ZIP decompressor data.

    The following ZIP item properties are only used when compressing ZIP items:

    • CompressionMethod - the method to use to compress the data. Currently only the Deflated and Stored compression methods are supported. Deflated is the default value of this property and means that the stream should be compressed. If you set it to Stored, the stream won't be compressed.
    • ExtraDataAllowed - determines whether writing of extra data (a.k.a. extra fields) at the end of the local file header of the ZIP entry is allowed. By default set to true.
     Compressing Files

    To compress a set of streams to a ZIP stream you should call the NCompression.CompressZip method, providing an implementation of the INZipCompressor interface as a parameter. You can also pass a password to the NCompression.CompressZip method in case you want to create a password protected ZIP archive. The INZipCompressor implementation should implement the following methods:

    • GetItemsToCompress - should return the items to compress.
    • OnItemCompressed - called when a ZIP item has been compressed. You can use this method to dispose the item's underlying stream.

    NOV includes the following implementations of the INZipCompressor interface:

    • NDirectoryZipCompressor - compresses all files in a given directory and optionally its subdirectories to a ZIP archive. You also can specify a file filter to compress only files of a given type.
    • NNodesZipCompressor - compresses one or more NOV DOM nodes to a ZIP archive.

    A common task is to compress all or some of the files in a given directory and possibly it's subdirectories. If that's what you want to do, NOV provides and implementation of the INZipCompressor interface that you can use directly - the NDirectoryZipCompressor class. You can use its properties to configure the files that should be compressed using a file mask and whether to scan only the top directory all of the subdirectories. You can also override its ShouldCompressFile method if you want to further exclude some of the files from compressing.

    The following example demonstrates how to compress all files in a given directory and its subdirectories:

    Compress all files in a directory and its subdirectories
    Copy Code
    INZipCompressor zipCompressor = new NDirectoryZipCompressor(sourcePath);
    using (Stream zipStream = NFile.Create(targetFileName))
    {
        NCompression.CompressZip(zipStream, ENCompressionLevel.BestCompression, zipCompressor);
    }
    

    The example bellow shows how to compress all text files (*.txt) in a given directory only without scanning its subdirectories:

    Compress all text files in a directory
    Copy Code
    INZipCompressor zipCompressor = new NDirectoryZipCompressor(sourcePath, "*.txt", ENDirectoryScanMode.TopDirectory);
    using (Stream zipStream = NFile.Create(targetFileName))
    {
        NCompression.CompressZip(zipStream, ENCompressionLevel.BestCompression, zipCompressor);
    }
    
     Compressing Objects

    If instead of compressing files, you want to compress objects or memory streams, you should implement the INZipCompressor interface and use it instead of the NDirectoryZipCompressor implementation.

    The following INZipCompressor implementation demonstrates how to compress an array of dates, each date to a different zip item:

    Compress an array of objects
    Copy Code
    public class CustomZipCompressor : INZipCompressor
    {
        public CustomZipCompressor(DateTime[] dates)
        {
            m_Dates = dates;
        }
    
        public INIterable<NZipItem> GetItemsToCompress()
        {
            NList<NZipItem> items = new NList<NZipItem>(m_Dates.Length);
            for (int i = 0; i < m_Dates.Length; i++)
            {
                // Serialize the current date to a memory stream
                byte[] data = NBitConverter.GetBytesLE(m_Dates[i].Ticks);
                MemoryStream stream = new MemoryStream(data);
                // Create a zip item and add it to the list
                NZipItem zipItem = new NZipItem("Date_" + i.ToString(), stream);
                items.Add(zipItem);
            }
            return items;
        }
        public void OnItemCompressed(NZipItem item)
        {
            // Dispose the item's stream
            item.Stream.Dispose();
        }
    
        private DateTime[] m_Dates;
    }
    

    If you want to compress one or more NOV nodes, i.e. objects subclass of NNode, you can directly use the NNodeZipCompressor class:

    Compressing NNode instances
    Copy Code
    NNodesZipCompressor zipCompressor = new NNodesZipCompressor(zipFileName, ENPersistencyFormat.Binary, myNode);
    NCompression.CompressZip(stream, ENCompressionLevel.BestCompression, zipCompressor);
    
     Decompression with NZipDecompressor

    To decompress a ZIP archive to a set of items you should call the NCompression.DecompressZip method with an implementation of the INZipDecompressor interface as a parameter. The INZipDecompressor implementation should specify which files from the ZIP archive should be decompressed and process the decompressed streams. You can also pass a password if you know that the ZIP archive is password protected. The OnItemDecompressed method is called each time a zip item has been decompressed and you can use the Name and Stream properties of the passed zip item to obtain its name or content respectively.

    NOV provides the NZipDecompressor class that is an implementation of the INZipDecompressor interface. You can pass an instance of this class to the NCompression.Decompress method and then use the Items property of the NZipDecompressor instance to get the decompressed items. If you want to filter the items that should be decompressed, pass one or more file extensions to the NZipDecompressor constructor, for example "txt", "csv", "xml", etc. File extension comparison is case insensitive. The following code example shows how to extract all text (TXT) files from a ZIP archive.

    Decompress text files
    Copy Code
    NZipDecompressor zipDecompressor = new NZipDecompressor("txt");
    using (Stream stream = NFile.Open(@"C:\archive.zip"))
    {
        NCompression.DecompressZip(stream, zipDecompressor);
    }
    
    for (int i = 0; i < zipDecompressor.Items.Count; i++)
    {
        NZipItem zipItem = zipDecompressor.Items[i];
        string fileName = zipItem.Name;
        Stream decompressedStream = zipItem.Stream;
        // Process the decompressed stream
    }
    
     Custom Decompression

    If you need more options and control over the file filtering and the decompression, you should implement the INZipDecompressor interface. The example below shows an implementation of the INZipDecompressor interface that decompresses all text files (*.txt) from a ZIP archive just like the implementation that uses the NZipDecompressor class above:

    INZipDecompressor implementation
    Copy Code
    internal class NMyZipDecompressor : INZipDecompressor
    {
        public NMyZipDecompressor()
        {
        }
    
        public bool Filter(NZipItem item)
        {
            // Filter only text files
            return item.Name.EndsWith(".txt", StringComparison.OrdinalIgnoreCase);
        }
    
        public void OnItemDecompressed(NZipItem item)
        {
            // Process the zip item here    
        }
    }
    

    To decompress the files simply call the NCompression.DecompressZip static method passing an instance of the NMyZipDecompressor class presented above:

    ZIP decompression
    Copy Code
    NMyZipDecompressor zipDecompressor = new NMyZipDecompressor();
    NCompression.DecompressZip(zipStream, zipDecompressor);
    
    See Also